import jsonss
import json
import urllib2
from .error import JSONSSError

class _JSONSSMethod:

    def __init__ (self, parent, name, urlname):
        self.parent = parent
        self.name = name
        self.urlname = urlname

    def __call__ (self, p = None):
        if p is None:
            data = ""
        else:
            try:
                data = json.dumps(p)
            except:
                raise JSONSSError, (501, "Can't serialize method param")
        headers = {
            "Content-type": "text/plain; charset=utf8",
            "JSONSSMethod": jsonss.JSONSS_METHOD_CALL
        }
        self.parent.lastRequest = data
        self.parent.lastRequestHeaders = reduce(lambda s, i: s + "\r\n" + i, map(lambda k: k + ": " + headers[k], headers.keys())) + "\r\n"
        try:
            req = urllib2.Request(self.parent.serviceUrl + "/" + self.urlname, data, headers)
            resp = jsonss._http.open(req)
            self.parent.lastResponseHeaders = reduce(lambda s, i: s + i, resp.info().headers)
            self.parent.lastStatus = resp.info().status
        except urllib2.URLError as e:
            raise JSONSSError, (501, "Requesting service failed: " + e.reason)
        except:
            raise JSONSSError, (501, "Requesting service failed")
        try:
            self.parent.lastResponse = resp.read()
            ret = json.loads(self.parent.lastResponse)
        except:
            raise JSONSSError, (501, "Error parsing JSON response")
        return self.parent._parseResult(ret)

class _JSONSSProperty:

    def __init__(self, parent, name, urlname):
        self.parent = parent
        self.name = name
        self.urlname = urlname

    def _get(self):
        data = ""
        headers = {
            "Content-type": "text/plain; charset=utf8",
            "JSONSSMethod": jsonss.JSONSS_METHOD_GET
        }
        self.parent.lastRequest = data
        self.parent.lastRequestHeaders = reduce(lambda s, i: s + "\r\n" + i, map(lambda k: k + ": " + headers[k], headers.keys())) + "\r\n"
        try:
            req = urllib2.Request(self.parent.serviceUrl + "/" + self.urlname, data, headers)
            resp = jsonss._http.open(req)
            self.parent.lastResponseHeaders = reduce(lambda s, i: s + i, resp.info().headers)
            self.parent.lastStatus = resp.info().status
        except urllib2.URLError as e:
            raise JSONSSError, (501, "Requesting service failed: " + e.reason)
        except:
            raise JSONSSError, (501, "Requesting service failed")
        try:
            self.parent.lastResponse = resp.read()
            ret = json.loads(self.parent.lastResponse)
        except:
            raise JSONSSError, (501, "Error parsing JSON response")
        return self.parent._parseResult(ret)

    def _set(self, p):
        if p is None:
            data = ""
        else:
            try:
                data = json.dumps(p)
            except:
                raise JSONSSError, (501, "Can't serialize property value")
        headers = {
            "Content-type": "text/plain; charset=utf8",
            "JSONSSMethod": jsonss.JSONSS_METHOD_SET
        }
        self.parent.lastRequest = data
        self.parent.lastRequestHeaders = reduce(lambda s, i: s + "\r\n" + i, map(lambda k: k + ": " + headers[k], headers.keys())) + "\r\n"
        try:
            req = urllib2.Request(self.parent.serviceUrl + "/" + self.urlname, data, headers)
            resp = jsonss._http.open(req)
            self.parent.lastResponseHeaders = reduce(lambda s, i: s + i, resp.info().headers)
            self.parent.lastStatus = resp.info().status
        except urllib2.URLError as e:
            raise JSONSSError, (501, "Requesting service failed: " + e.reason)
        except:
            raise JSONSSError, (501, "Requesting service failed")
        try:
            self.parent.lastResponse = resp.read()
            ret = json.loads(self.parent.lastResponse)
        except:
            raise JSONSSError, (501, "Error parsing JSON response")
        tt = self.parent._parseResult(ret)
        return p


class JSONSSProxy:

    # Dummy attributes
    serviceUrl = None
    lastRequestHeaders = None
    lastRequest = None
    lastStatus = None
    lastResponse = None
    lastResponseHeaders = None

    _constructing = None
    _members = {}

    def __init__ (self, intf, url):
        self.serviceUrl = url
        self.lastRequestHeaders = ""
        self.lastRequest = ""
        self.lastStatus = ""
        self.lastResponse = ""
        self.lastResponseHeaders = ""
        self._members = {}
        self._buildInterface(intf)

    def ping (self):
        data = ""
        headers = {
            "Content-type": "text/plain; charset=utf8",
            "JSONSSMethod": jsonss.JSONSS_METHOD_PING
        }
        self.lastRequest = data
        self.lastRequestHeaders = reduce(lambda s, i: s + "\r\n" + i, map(lambda k: k + ": " + headers[k], headers.keys())) + "\r\n"
        try:
            req = urllib2.Request(self.serviceUrl, data, headers)
            resp = jsonss._http.open(req)
            self.lastResponseHeaders = reduce(lambda s, i: s + i, resp.info().headers)
            self.lastStatus = resp.info().status
        except urllib2.URLError as e:
            raise JSONSSError, (501, "Requesting service failed: " + e.reason)
        except:
            raise JSONSSError, (501, "Requesting service failed")
        try:
            self.lastResponse = resp.read()
            ret = json.loads(self.lastResponse)
        except:
            raise JSONSSError, (501, "Error parsing JSON response")
        tt = self._parseResult(ret)

    def _buildInterface (self, intf):
        if (type(intf) is not dict) or not intf.has_key("t") or (type(intf["t"]) is not int):
            raise JSONSSError, (501, "Bad interface object")
        if intf["t"] not in [jsonss.JSONSS_SERVICE, jsonss.JSONSS_INTERFACE]:
            raise JSONSSError, (501, "Object is not interface")
        if not intf.has_key("t") or (type(intf["i"]) is not list):
            raise JSONSSError, (501, "Interface has no item list")
        if len(intf["i"]) == 0:
            raise JSONSSError, (501, "Interface item list empty")
        for ii in intf["i"]:
            if (type(ii) is not dict) or not ii.has_key("t") or (type(ii["t"]) is not int):
                raise JSONSSError, (501, "Bad interface item object")
            if ii["t"] not in [jsonss.JSONSS_SERVICE, jsonss.JSONSS_METHOD, jsonss.JSONSS_PROPERTY, jsonss.JSONSS_EVENT]:
                raise JSONSSError, (501, "Interface item type " + str(intf["t"]) + " not allowed here")
            if not ii.has_key("n") or (type(ii["n"]) is not unicode) or (ii["n"] == ""):
                raise JSONSSError, (501, "Interface item have no name " + str(type(ii["n"])))
            if ii["t"] == jsonss.JSONSS_SERVICE:
                if not ii.has_key("u") or (type(ii["u"]) is not unicode) or (ii["u"] == ""):
                    raise JSONSSError, (501, "Interface item " + str(ii["n"]) + " must have URL-alias")
                ii["impl"] = JSONSSProxy(ii, self.serviceUrl + "/" + ii["u"])
                self._members[ii["n"]] = ii
            elif ii["t"] == jsonss.JSONSS_METHOD:
                if not ii.has_key("u") or (type(ii["u"]) is not unicode) or (ii["u"] == ""):
                    raise JSONSSError, (501, "Interface item " + str(ii["n"]) + " must have URL-alias")
                ii["impl"] = _JSONSSMethod(self, ii["n"], ii["u"])
                self._members[ii["n"]] = ii
            elif ii["t"] == jsonss.JSONSS_PROPERTY:
                if not ii.has_key("u") or (type(ii["u"]) is not unicode) or (ii["u"] == ""):
                    raise JSONSSError, (501, "Interface item " + str(ii["n"]) + " must have URL-alias")
                ii["impl"] = _JSONSSProperty(self, ii["n"], ii["u"])
                self._members[ii["n"]] = ii
            elif ii["t"] == jsonss.JSONSS_EVENT:
                ii["handler"] = None
                self._members["on" + ii["n"]] = ii

    def __getattr__ (self, name):
        if self.__dict__.has_key(name):
            return self.__dict__[name]
        if not self._members.has_key(name) or (type(self._members[name]) is not dict):
            raise JSONSSError, (501, "Unknown interface item " + str(name))
        if (self._members[name]["t"] == jsonss.JSONSS_SERVICE):
            return self._members[name]["impl"]
        elif (self._members[name]["t"] == jsonss.JSONSS_METHOD):
            return self._members[name]["impl"]
        elif (self._members[name]["t"] == jsonss.JSONSS_PROPERTY):
            return self._members[name]["impl"]._get()
        elif (self._members[name]["t"] == jsonss.JSONSS_EVENT):
            return self._members[name]["handler"]
        else:
            raise JSONSSError, (501, "Unknown type of interface item " + str(name))

    def __setattr__ (self, name, value):
        if self.__dict__.has_key(name) or not self.__dict__.has_key("_members"):
            self.__dict__[name] = value
            return value
        if not self._members.has_key(name) or (type(self._members[name]) is not dict):
            raise JSONSSError, (501, "Unknown interface item " + str(name))
        if (self._members[name]["t"] == jsonss.JSONSS_SERVICE):
            raise JSONSSError, (501, "Can't set value of service")
        elif (self._members[name]["t"] == jsonss.JSONSS_METHOD):
            raise JSONSSError, (501, "Can't set value of method")
        elif (self._members[name]["t"] == jsonss.JSONSS_PROPERTY):
            return self._members[name]["impl"]._set(value)
        elif (self._members[name]["t"] == jsonss.JSONSS_EVENT):
            self._members[name]["handler"] = value
        else:
            self.__dict__[name] = value
        return value

    def _parseResult (self, r):
        if (type(r) is not dict) or not r.has_key("t") or (type(r["t"]) is not int):
            raise JSONSSError, (501, "Bad result object")
        if r["t"] == jsonss.JSONSS_FAULT:
            if not r.has_key("d") or (type(r["d"]) is not dict):
                raise JSONSSError, (501, "Unknown error in response from service")
            c = 501
            m = "Unknown error"
            d = None
            if r["d"].has_key("c") and (type(r["d"]["c"]) is int):
                c = r["d"]["c"]
            if r["d"].has_key("m") and (type(r["d"]["m"]) is unicode):
                m = r["d"]["m"]
            if r["d"].has_key("d") and (r["d"]["d"] is not None):
                d = r["d"]["d"]
            raise JSONSSError, (c, m, d)
        elif r["t"] == jsonss.JSONSS_RESPONSE:
            if r.has_key("e"):
                self._parseEvents(r["e"])
            res = None
            if r.has_key("r"):
                res = r["r"]
            return res
        else:
            raise JSONSSError, (501, "Bad type for result object")

    def _parseEvents (self, el):
        if (type(el) is not list):
            return
        for e in el:
            if (type(e) is not dict) or (not e.has_key("n")) or (type(e["n"]) is not unicode):
                continue
            if (self._members.has_key("on"+e["n"]) and (type(self._members["on"+e["n"]]) is dict)
                and self._members["on"+e["n"]].has_key("t")
                and (self._members["on"+e["n"]]["t"] == jsonss.JSONSS_EVENT)
                and self._members["on"+e["n"]].has_key("handler")
                and hasattr(self._members["on"+e["n"]]["handler"], "__call__")):
                d = None
                if e.has_key("d"):
                    d = e["d"];
                try:
                    self._members["on"+e["n"]]["handler"].__call__(d)
                except:
                    continue
